﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace GeoSketch2.Core
{
    /// <summary>
    /// The viewport object, contains the control on wich the object will be draw, and the
    /// transformation to transform objects-coordinates to images-coordinates
    /// </summary>
    [Serializable]
    public class BaseViewport
    {
        #region Fields
        /// <summary>
        /// The objects to display
        /// </summary>
        List<BaseDrawableObject> _GeoObjects;
        /// <summary>
        /// The selected objects
        /// </summary>
        public List<BaseDrawableObject> SelectedObjects;
        /// <summary>
        /// The current layer
        /// </summary>
        Layer _CurrentLayer;

        List<LineType> _LineTypes;
        /// <summary>
        /// The current linetype
        /// </summary>
        public LineType CurrentLineType;
        /// <summary>
        /// The current color
        /// </summary>
        public Color CurrentColor;
        /// <summary>
        /// The layers' list
        /// </summary>
        List<Layer> _Layers;
        /// <summary>
        /// The graphics object of the control
        /// </summary>
        public Graphics Graphic;
        /// <summary>
        /// The display control
        /// </summary>
        public BaseGeoDisplayBox DisplayControl;
        /// <summary>
        /// The transformation to transform objects-coordinates to images-coordinates
        /// </summary>
        public Transformations.AffineTransformation2D Object2ImageTransformation;

        private Bitmap _BackBufferImage;
        
        private RectangleD _CurrentObjectView;

        /// <summary>
        /// Indicates if the display is using back buffer, for smoothing
        /// </summary>
        bool _DoubleBuffered;
        #endregion

        #region Constructor

        /// <summary>
        /// The viewport object, contains the control on wich the object will be drawn, and the
        /// transformation to transform objects-coordinates to images-coordinates
        /// </summary>
        /// <param name="Control">The display control</param>
        public BaseViewport(BaseGeoDisplayBox Control)
            : this(Control, new RectangleD())
        {
        }
        
        /// <summary>
        /// The viewport object, contains the control on wich the object will be drawn, and the
        /// transformation to transform objects-coordinates to images-coordinates
        /// </summary>
        /// <param name="Control">The display control</param>
        /// <param name="CurrentObjectView">The rectangle which wraps the object area to be displayed</param>
        public BaseViewport(BaseGeoDisplayBox Control, RectangleD CurrentObjectView)
        {
            Object2ImageTransformation = new Transformations.AffineTransformation2D();
            this.DisplayControl = Control;
            this._CurrentObjectView = CurrentObjectView;
            ComputeTransformation();
            this._GeoObjects = new List<BaseDrawableObject>();
            this.SelectedObjects = new List<BaseDrawableObject>();
            _DoubleBuffered = false;
            DisplayControl_Resize(null, null);
            _Layers = new List<Layer>();
            Layer Layer0 = new Layer("0", Color.Black, new LineType(), true);
            _Layers.Add(Layer0);
            CurrentLayer = Layer0;
            DisplayControl.Resize += new EventHandler(DisplayControl_Resize);
        }

        
        #endregion

        #region Events
        void DisplayControl_Resize(object sender, EventArgs e)
        {
            if (_DoubleBuffered)
            {
                _BackBufferImage = new Bitmap(DisplayControl.ClientRectangle.Width, DisplayControl.ClientRectangle.Height, PixelFormat.Format32bppPArgb);
            }
            ComputeTransformation();
            SetupGraphics();
        } 
        #endregion

        #region Methods
        /// <summary>
        /// Computes the transformation regarding the rectangle of the currentview
        /// </summary>
        public void ComputeTransformation()
        {
            Object2ImageTransformation.ComputeTranformationFromTargetArea(_CurrentObjectView, DisplayControl.ClientRectangle);
        }

        /// <summary>
        /// Sets the graphic object, to draw objects
        /// </summary>
        protected void SetupGraphics()
        {
            if (_DoubleBuffered) { Graphic = System.Drawing.Graphics.FromImage(_BackBufferImage); }
            else { Graphic = DisplayControl.CreateGraphics(); }
            Graphic.InterpolationMode = InterpolationMode.NearestNeighbor;
            Graphic.CompositingQuality = CompositingQuality.HighSpeed;
            Graphic.SmoothingMode = SmoothingMode.HighQuality;
            Graphic.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            Graphic.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
            SetupGraphicsClip();
        }

        /// <summary>
        /// Sets the drawing bounds of the graphic object
        /// </summary>
        protected void SetupGraphicsClip()
        {
            Graphic.Clip = DisplayBounds;
        }

        /// <summary>
        /// The method to clear the display
        /// </summary>
        public virtual void ClearScene()
        {
            Graphic.Clear(Vars.Setup.StandardObjects.Viewport.BackgroundColor);
        }

        /// <summary>
        /// Displays the scene, for double buffer purpose
        /// </summary>
        public void DisplayScene()
        {
            if (_DoubleBuffered)
            {
                DisplayControl.BackgroundImage = _BackBufferImage;
            }
        }

        /// <summary>
        /// Sets the next layer display order available
        /// </summary>
        public void SetNextLayerId()
        {
            List<Layer> layers_sorted = (List<Layer>)Layers.DeepClone();
            Layers.Sort(Layer.CompareLayerDisplayOrderDesc);
            Vars.Setup.StandardObjects.Layer.NextAvailableDisplayOrder = layers_sorted[0].DisplayOrder + 1;
            layers_sorted = null;
        }

        /// <summary>
        /// Sorts the layers according to their display order
        /// </summary>
        public void SortLayers()
        {
            Layers.Sort(Layer.CompareLayerDisplayOrderAsc);
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the current layer
        /// </summary>
        public Layer CurrentLayer
        {
            get
            {
                return _CurrentLayer;
            }
            set
            {
                _CurrentLayer = value;
                CurrentLineType = null;
                CurrentColor = Color.Empty;
            }
        }

        /// <summary>
        /// Gets or sets the objects
        /// </summary>
        public virtual List<BaseDrawableObject> GeoObjects
        {
            get
            {
                return _GeoObjects;
            }
            set
            {
                _GeoObjects = value; ;
            }
        }

        /// <summary>
        /// Gets or sets the layers
        /// </summary>
        public virtual List<Layer> Layers
        {
            get
            {
                return _Layers;
            }
            set
            {
                _Layers = value; ;
            }
        }

        /// <summary>
        /// Gets or sets the library of linetypes
        /// </summary>
        public virtual List<LineType> LineTypeLibrary
        {
            get
            {
                return _LineTypes;
            }
            set
            {
                _LineTypes = value;
            }
        }

        /// <summary>
        /// Gets or sets the rectangle which wraps the object area to be displayed
        /// </summary>
        public RectangleD CurrentObjectView
        {
            get
            {
                return _CurrentObjectView;
            }
            set
            {
                if (value.Height > Vars.Setup.StandardObjects.Viewport.CurrentViewMinimumSize && value.Width > Vars.Setup.StandardObjects.Viewport.CurrentViewMinimumSize)
                {
                    
                    _CurrentObjectView = value;
                    ComputeTransformation();
                    SetupGraphicsClip();
                }
            }
        }

        /// <summary>
        /// Gets or sets the double buffer function for smoothing purpose
        /// </summary>
        public bool DoubleBuffereEnabled
        {
            get
            {
                return _DoubleBuffered;
            }
            set
            {
                _DoubleBuffered = value;
                DisplayControl.DoubleBuffered = value;
                DisplayControl_Resize(null, null);
            }
        }

        // <summary>
        /// Gets the display bounds
        // </summary>
        protected virtual Region DisplayBounds
        {
            get
            {
                RectangleD r = DisplayControl.ClientRectangle;
                return new Region(new GraphicsPath(
                    new PointF[]{
                        r.UpperLeftPoint,
                        r.UpperRightPoint,
                        r.LowerRightPoint,
                        r.LowerLeftPoint
                    },
                    new byte[]{
                        (byte)PathPointType.Start,
                        (byte)PathPointType.Line,
                        (byte)PathPointType.Line,
                        (byte)PathPointType.Line}));
                    
            }
        }
        #endregion
    }
}
